package basics;

import java.util.Iterator;

import basics.VectorMatrixUtils.OperationOpt;

public class DenseMatrix implements Matrix, DenseVectorMatrix {

	// private DenseVector[] matrix;

	Jama.Matrix innerMatrix;

	public DenseMatrix(int n, int m) {
		innerMatrix = new Jama.Matrix(n, m);
	}

	public DenseMatrix(double[][] array) {
		innerMatrix = new Jama.Matrix(array);
	}

	DenseMatrix(Jama.Matrix newMatrix) {
		innerMatrix = newMatrix;
	}

	public DenseMatrix(DenseMatrix m) {
		this(m.innerMatrix.getArray());
	}

	public int length() {
		return length(1);
	}

	public int length(int d) {
		if (d == 1) {
			return innerMatrix.getRowDimension();
		}
		return innerMatrix.getColumnDimension();
	}

	public VectorMatrix minus(VectorMatrix m) {
		Matrix a = new DenseMatrix(innerMatrix.minus(((DenseVectorMatrix) m).getInnerMatrix()).getArray());
		return a;
	}

	public VectorMatrix minusEqual(VectorMatrix m) {
		innerMatrix.minusEquals(((DenseMatrix) m).innerMatrix);
		return this;
	}

	public VectorMatrix divideEqual(double d) {
		return times(1. / d);
	}

	public Matrix times(VectorMatrix m) {
		Matrix a = new DenseMatrix(innerMatrix.times(((DenseVectorMatrix) m).getInnerMatrix()).getArray());
		return a;
	}

	public Matrix timesEqual(VectorMatrix m) {
		innerMatrix = innerMatrix.times(((DenseVectorMatrix) m).getInnerMatrix());
		return this;
	}

	public VectorMatrix add(VectorMatrix m) {
		Matrix a = new DenseMatrix(innerMatrix.plus(((DenseVectorMatrix) m).getInnerMatrix()).getArray());
		return a;
	}

	public VectorMatrix addEqual(VectorMatrix m) {
		innerMatrix.plusEquals(((DenseMatrix) m).innerMatrix);
		return this;
	}

	public double det() {
		return innerMatrix.det();
	}

	public Matrix inverse() {
		return new DenseMatrix(innerMatrix.inverse().getArray());
	}

	public Matrix transpose() {
		return new DenseMatrix(innerMatrix.transpose().getArray());
	}

	public VectorMatrix times(double d) {
		innerMatrix.timesEquals(d);
		return this;
	}

	public double get(int... i) {
		return innerMatrix.get(i[0], i[1]);
	}

	public void set(double v, int... i) {
		innerMatrix.set(i[0], i[1], v);
	}

	public VectorMatrix timesCopy(double d) {
		// TODO Auto-generated method stub
		return null;
	}

	public Matrix pow(int n) {
		Matrix ret = new DenseMatrix(this.innerMatrix.getArray());

		for (int i = 1; i < n; i++) {
			ret.timesEqual(this);
		}

		return ret;
	}

	@Override
	public VectorMatrix divide(VectorMatrix a) {
		return times(((DenseMatrix) a).inverse());
	}

	@Override
	public Jama.Matrix getInnerMatrix() {
		return innerMatrix;
	}

	@Override
	public Vector getVector(int i) {
		return new DenseVector(innerMatrix.getMatrix(0, innerMatrix.getRowDimension() - 1, i, i));
	}

	@Override
	public double sumAllElements() {
		return sumAllElements(OperationOpt.Default);
	}

	@Override
	public double sumAllElements(VectorMatrixUtils.OperationOpt opt) {
		double s = 0;

		for (int i = 0; i < length(1); i++) {
			for (int j = 0; j < length(2); j++) {
				if ((opt == OperationOpt.Default) || (opt == OperationOpt.NonZero && get(i, j) != 0)) {
					s += get(i, j);
				} else if (opt == OperationOpt.AbsVal) {
					s += Math.abs(get(i, j));
				}
			}
		}

		return s;
	}

	@Override
	public Iterator<Double> iterator() {
		return VectorMatrixUtils.getIterator(this);
	}

	@Override
	public int dimesnsion() {
		return 2;
	}

	@Override
	public double max() {
		return VectorMatrixUtils.max(this);
	}

	public String toString() {
		return VectorMatrixUtils.toString(this);
	}

	@Override
	public Vector getColumnVector() {
		return new DenseVector(innerMatrix.getColumnPackedCopy());
	}

	@Override
	public Vector getRowVector() {
		return new DenseVector(innerMatrix.getRowPackedCopy());
	}

	public static DenseMatrix getIdentityMatrix(int n) {
		return getIdentityMatrix(n, n);
	}

	public static DenseMatrix getIdentityMatrix(int m, int n) {
		return new DenseMatrix(Jama.Matrix.identity(m, n));
	}

	@Override
	public double norm2() {
		return innerMatrix.norm2();
	}
}
